﻿/// <reference path="../Libraries/jasmine/jasmine.d.ts" />
/// <reference path="../ClassManager.ts" />

declare module jasmine {
    interface Matchers {
        toHaveOwnProperty(expected: string);
        toBeTypeOf(expected: string);
    }
}

describe('ExtSpec.ClassManager', function () {
    'use strict';

    var manager = ExtSpec.ClassManager,
        matchers = {
            toHaveOwnProperty: function () {
                return {
                    compare: function (actual, expected) {
                        return {
                            pass: actual.hasOwnProperty(expected)
                        };
                    }
                }
            },

            toBeTypeOf: function (expected) {
                return {
                    compare: function (actual, expected) {
                        return {
                            pass: ExtSpec.isTypeOf(actual, expected)
                        };
                    }
                }
            }
        };

    beforeEach(function () {
        jasmine.addMatchers(matchers);
    });

    describe('construct', function () {
        it('should add statics to a constructor', function () {
            var actual,
                definition = {
                    statics: {
                        doSomething: function () { }
                    }
                };

            manager.set('Not.a.Class', definition);

            actual = manager.construct('Not.a.Class');

            expect(actual).toHaveOwnProperty('doSomething');
            expect(actual.doSomething).toBeTypeOf('function');
        });

        it('should add inheritable statics to a constructor', function () {
            var actual,
                definition = {
                    inheritableStatics: {
                        doSomethingElse: function () { }
                    }
                };

            manager.set('Not.a.Class', definition);

            actual = manager.construct('Not.a.Class');

            expect(actual).toHaveOwnProperty('doSomethingElse');
            expect(actual.doSomethingElse).toBeTypeOf('function');
        });

        it('should add non statics to a constructor prototype', function () {
            var actual,
                definition = {
                    aProperty: 99.99,
                    aMethod: function () { }
                };

            manager.set('Not.a.Class', definition);

            actual = manager.construct('Not.a.Class');

            expect(actual.prototype).toHaveOwnProperty('aProperty');
            expect(actual.prototype.aProperty).toBe(99.99);
            expect(actual.prototype.aMethod).toBeTypeOf('function');
        });

        it('should not add statics to a constructor prototype', function () {
            var actual,
                definition = {
                    aProperty: 99.99,
                    aMethod: function () { },
                    statics: {
                        doSomething: function () { }
                    },
                    inheritableStatics: {
                        doSomethingElse: function () { }
                    }
                };

            manager.set('Not.a.Class', definition);

            actual = manager.construct('Not.a.Class');

            expect(actual.prototype).not.toHaveOwnProperty('doSomething');
            expect(actual.prototype).not.toHaveOwnProperty('doSomethingElse');
        });
    });

    describe('create', function () {
        it('should create an instance of a class', function () {
            var actual,
                proto,
                definition = {
                    alias: 'jolt.cola',
                    zap: function () { }
                };

            manager.set('Not.a.Class', definition);

            actual = manager.create('Not.a.Class');

            proto = Object.getPrototypeOf(actual);
            expect(actual).not.toHaveOwnProperty('zap');
            expect(proto).toHaveOwnProperty('zap');
            expect(actual.alias).toBe('jolt.cola');
        });

        it('should create an instance of a class with a custom constructor', function () {
            var actual,
                definition = {
                    constructor: function () {
                        this.instanceProperty = 'yoghurt';
                    }
                };

            manager.set('Not.a.Class', definition);

            actual = manager.create('Not.a.Class');

            expect(actual).toHaveOwnProperty('instanceProperty');
            expect(actual.instanceProperty).toBe('yoghurt');
        });

        it('should create an instance of a function form class', function () {
            var actual,
                proto;

            manager.set('Not.a.Class', function () {
                return {
                    phaser: function () {
                        return 'stun';
                    }
                };
            });

            actual = manager.create('Not.a.Class');

            proto = Object.getPrototypeOf(actual);
            expect(proto).toHaveOwnProperty('phaser');
            expect(actual.phaser()).toBe('stun');
        });

        it('should apply preConstruct before custom constructor', function () {
            var actual,
                definition = {
                    constructor: function () {
                        this.instanceProperty = 'calledfirst';
                    }
                },
                preConstruct = function () {
                    this.instanceProperty = 'notcalledfirst';
                    this.fakeMethod = jasmine.createSpy('fakeMethod');
                };

            manager.set('Not.a.Class', definition);

            actual = manager.create('Not.a.Class', preConstruct);

            expect(actual.fakeMethod).toBeDefined();
            expect(actual.instanceProperty).toBe('calledfirst');
        });
    });
});